package com.ams.protocol.rtmp.net;

import java.io.IOException;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ams.media.MediaMessage;
import com.ams.protocol.rtmp.amf.*;
import com.ams.protocol.rtmp.RtmpConnection;
import com.ams.protocol.rtmp.message.*;

public class NetStream {
    final private Logger logger = LoggerFactory.getLogger(NetStream.class);
    private RtmpConnection rtmp;
    private int chunkStreamId = 3;
    private int streamId;
    private String streamName;
    private int transactionId = 0;
    private long timestamp = 0;

    public NetStream(RtmpConnection rtmp, int streamId) {
        this.rtmp = rtmp;
        this.streamId = streamId;
    }

    public void writeMessage(RtmpMessage message) throws IOException {
        if (message instanceof RtmpMessageVideo) {
            rtmp.writeRtmpMessage(5, streamId, timestamp, message);
        } else if (message instanceof RtmpMessageAudio) {
            rtmp.writeRtmpMessage(6, streamId, timestamp, message);
        } else {
            rtmp.writeRtmpMessage(chunkStreamId, streamId, timestamp, message);
        }
    }

    public void writeMessage(long time, RtmpMessage message) throws IOException {
        rtmp.writeRtmpMessage(chunkStreamId, streamId, time, message);
    }

    public void sendMediaMessage(MediaMessage msg) throws IOException {
        setTimestamp(msg.getTimestamp());
        writeMessage(msg.toRtmpMessage());
    }
    
    public void writeStatusMessage(String status, AmfValue info)
            throws IOException {
        AmfValue value = AmfValue.newObject();
        value.put("level", "status").put("code", status);
        Map<String, AmfValue> v = info.object();
        for (String key : v.keySet()) {
            value.put(key, v.get(key).toString());
        }
        writeMessage(new RtmpMessageCommand("onStatus", transactionId,
                AmfValue.array(null, value)));
    }

    public void writeErrorMessage(String msg) throws IOException {
        AmfValue value = AmfValue.newObject();
        value.put("level", "error").put("code", "NetStream.Error")
                .put("details", msg);
        writeMessage(new RtmpMessageCommand("onStatus", transactionId,
                AmfValue.array(null, value)));
    }

    public void writeDataMessage(AmfValue[] values) throws IOException {
        writeMessage(new RtmpMessageData(AmfValue.toBinary(values)));
    }

    public void writeSeekMessage(int time) throws IOException {
        // clear
        rtmp.writeProtocolControlMessage(new RtmpMessageUserControl(
                RtmpMessageUserControl.EVT_STREAM_EOF, streamId));
        rtmp.writeProtocolControlMessage(new RtmpMessageUserControl(
                RtmpMessageUserControl.EVT_STREAM_IS_RECORDED, streamId));
        rtmp.writeProtocolControlMessage(new RtmpMessageUserControl(
                RtmpMessageUserControl.EVT_STREAM_BEGIN, streamId));

        writeStatusMessage(
                "NetStream.Seek.Notify",
                AmfValue.newObject()
                        .put("description", "Seeking " + time + ".")
                        .put("details", streamName).put("clientId", streamId));

        writeStatusMessage(
                "NetStream.Play.Start",
                AmfValue.newObject()
                        .put("description", "Start playing " + streamName + ".")
                        .put("clientId", streamId));

    }

    public void writePlayMessage() throws IOException {

        // set chunk size
        //rtmp.writeProtocolControlMessage(new RtmpMessageChunkSize(1024));

        // NetStream.Play.Reset
        writeStatusMessage(
                "NetStream.Play.Reset",
                AmfValue.newObject()
                        .put("description", "Resetting " + streamName + ".")
                        .put("details", streamName).put("clientId", streamId));

        // clear
        rtmp.writeProtocolControlMessage(new RtmpMessageUserControl(
                RtmpMessageUserControl.EVT_STREAM_IS_RECORDED, streamId));
        rtmp.writeProtocolControlMessage(new RtmpMessageUserControl(
                RtmpMessageUserControl.EVT_STREAM_BEGIN, streamId));

        // NetStream.Play.Start
        writeStatusMessage(
                "NetStream.Play.Start",
                AmfValue.newObject()
                        .put("description", "Start playing " + streamName + ".")
                        .put("clientId", streamId));

    }

    public void writeStopMessage() throws IOException {
        // clear
        rtmp.writeProtocolControlMessage(new RtmpMessageUserControl(
                RtmpMessageUserControl.EVT_STREAM_EOF, streamId));

        // NetStream.Play.Complete
        writeDataMessage(AmfValue.array("onPlayStatus", AmfValue.newObject()
                .put("level", "status").put("code", "NetStream.Play.Complete")));

        // NetStream.Play.Stop
        writeStatusMessage(
                "NetStream.Play.Stop",
                AmfValue.newObject()
                        .put("description",
                                "Stoped playing " + streamName + ".")
                        .put("clientId", streamId));
    }

    public void writePauseMessage(boolean pause) throws IOException {
        writeStatusMessage((pause ? "NetStream.Pause.Notify"
                : "NetStream.Unpause.Notify"), AmfValue.newObject());
    }

    public void writePublishMessage(String publishName) throws IOException {
        writeStatusMessage("NetStream.Publish.Start",
                AmfValue.newObject().put("details", publishName));
    }

    public void flush() throws IOException {
        rtmp.flush();
    }
    
    public void setStreamName(String streamName) {
        this.streamName = streamName;
    }

    public void setChunkStreamId(int chunkStreamId) {
        this.chunkStreamId = chunkStreamId;
    }

    public void setTransactionId(int transactionId) {
        this.transactionId = transactionId;
    }

    public int getStreamId() {
        return streamId;
    }

    public String getStreamName() {
        return streamName;
    }
    
    public long getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }

}